﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Linq;
using System.ServiceModel;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Threading;
using FinPlusBaseCore;
using FinPlusAssembler;
using FinPlusInterfaces;

namespace FinPlusConnService
{
    internal class FinPlusService
    {
        public IFinPlusComp Service { get; private set; }
        public bool Completed { get; private set; }
        public bool Started { get; private set; }

        private readonly ConcurrentDictionary<string, IPodCallback> _callbacks;
        private readonly string _guid, _serviceKey, _serviceType, _query, _configFolder;
        private readonly string[] _targetAssemblys;
        private readonly int _asyncWait, _asyncRetry;
        private readonly Env _env;
        private DateTime _started = DateTime.MinValue;
        private volatile object _lock = new object();

        //contruct
        public FinPlusService(Env env, string serviceKey, string serviceType, string query, string targetAssemblys, string configFolder, int asyncWait, int asyncRetry)
        {
            Started = false;
            _env = env;
            _serviceKey = serviceKey;
            _serviceType = serviceType;
            _query = query;
            _targetAssemblys = targetAssemblys.Split();
            _configFolder = configFolder;
            _asyncWait = asyncWait;
            _asyncRetry = asyncRetry;
            _guid = Guids.New();
            _callbacks = new ConcurrentDictionary<string, IPodCallback>();
        }

        //public
        public void Start()
        {
            try
            {
                if (!Started)
                    lock (_lock)
                         if (!Started)
                         {
                            _started = DateTime.Now;
                            Level.Info.Log(string.Format("Service {0} for pods {1}", _serviceType, _query));
                            var path = Paths.Values["FolderServices"] + _serviceType + ".service";
                            var pod = IOC.New<IPods>(path, "root/current", _query).Construct(_serviceType, _env);
                            Service = (IFinPlusComp)pod.Object;
                            Service.Adapter.PodUpdate += Bean_Changed;
                            Started = true;
                        }
            }
            catch (ServiceConnException e)
            {
                Level.Error.Log(e.Message, e);
                throw e;
            }
        }

        public void Stop()
        {
            try
            {
                if (Started)
                    lock (_lock)
                         if (Started)
                         {
                            Service.Dispose();
                            Completed= Started = false;
                         }
            }
            catch (ServiceConnException e)
            {
                Level.Error.Log(e.Message, e);
                throw e;
            }
        }

        public void Dispose(string guid)
        {
            IPodCallback callback = null;
            if (_callbacks.ContainsKey(guid))
                _callbacks.TryRemove(guid, out callback);
        }

        public void SendResults(string guid)
        {
            try
            {
                var callback = OperationContext.Current.GetCallbackChannel<IPodCallback>();
                _callbacks[guid] = callback;

                 foreach (var pod in Service.Adapter.Values)
                     callback.DispatchPod(_env, _serviceKey, guid, pod.Cast<Bean>(), ChangeType.New);

                callback.DispatchCompleted(_env, _serviceKey, guid);
                Completed = true;
            }
            catch (ServiceConnException e)
            {
                Level.Error.Log(e.Message, e);
            }
        }

        public void WaitCompleted()
        {
            for (int x = 0; x < _asyncRetry; x++)
            {
                Thread.SpinWait(_asyncWait);
                if (Completed)
                    break;
            }
        }

        public IEnumerable<Bean> Info()
        {
            var info = new Pod();
            info.Add(PodField.Id.ToString(), -1);
            info.Add(PodField.Type.ToString(), "Service");
            info.Add(PodField.TimeStamp.ToString(), _started);
            info.Add("Guid", _guid);
            info.Add("ServiceKey", _serviceKey);
            info.Add("NumConn",_callbacks.Count);
            info.Add("Started", Started);
            info.Add("Completed", Completed);
            info.Add("ServiceType", _serviceType);
            info.Add("Query", _query);
            info.Add("ConfigFolder", _configFolder);
            info.Add("TargetAssemblys", _targetAssemblys.StringFrom1d(','));
            info.Add("AsyncWait", _asyncWait);
            info.Add("AsyncRetry", _asyncWait);
            info.Add("Env", _env.ToString());

            return info.ToList().ConvertAll(b => (Bean)b);
        }

        //events
        private void Bean_Changed(object s, BeanEventArgs a)
        {
            try
            {
                var pod = (IPod)s;
                foreach (var callback in _callbacks)
                    if (a.ChangeType.Equals(ChangeType.Altered))
                        callback.Value.DispatchBean(_env, _serviceKey, callback.Key, pod.Id, (Bean)a.Bean, a.ChangeType);
                    else
                        callback.Value.DispatchPod(_env, _serviceKey, callback.Key, pod.Cast<Bean>(), a.ChangeType);
            }
            catch (ServiceConnException e)
            {
                Level.Error.Log(e.Message, e);
            }
        }
    }
}
